// SPDX-License-Identifier: GPL-2.0
#include <linux/module.h>
#include <linux/init.h>
#include <linux/platform_device.h>
#include <linux/gpio.h>
#include <linux/usb.h>
#include <linux/dma-map-ops.h>
#include <linux/dma-mapping.h>
#include <linux/io.h>
#include "hiusb_ont.h"

static struct usb_logic_port g_hal_usb_logic_port;

#ifdef CONFIG_ARM
extern const struct dma_map_ops arm_coherent_dma_ops;
#endif

static enum HW_CHIP_ID_E hw_chip_id;
enum HW_CHIP_ID_E noehci_id[] = {
	HW_CHIP_ID_5118_E,
	HW_CHIP_ID_5118V2_E,
	HW_CHIP_ID_5116T_E,
	HW_CHIP_ID_5116TV5_E,
	HW_CHIP_ID_5610T_E,
	HW_CHIP_ID_5117V_E,
	HW_CHIP_ID_5182H_E,
	HAL_CHIP_ID_5182HV5,
	HAL_CHIP_ID_5182T,
	HAL_CHIP_ID_1156H,
	HAL_CHIP_ID_1156E,
	HAL_CHIP_ID_1155H,
	HAL_CHIP_ID_2952H,
};

#ifdef CONFIG_ARCH_HISI
static enum HW_CHIP_ID_E get_chip_id(void)
{
	unsigned int *pui_reg = 0;
	unsigned int ui_ret = HW_CHIP_ID_MPW_E;
	unsigned int ui_st_num = 0;
	unsigned int ui_loop = 0;
	uint32_t reg_val;

	struct hw_ker_chipid_info_s st_chipid_info[] = {
		{CHIP_ID_SERIAL_VERSION_MASK, CHIP_ID_SD5115S,   HW_CHIP_ID_5115S_E},
		{CHIP_ID_SERIAL_VERSION_MASK, CHIP_ID_SD5115H,   HW_CHIP_ID_5115H_E},
		{CHIP_ID_SERIAL_VERSION_MASK, CHIP_ID_SD5115T,   HW_CHIP_ID_5115T_E},
		{CHIP_ID_SERIAL_VERSION_MASK, CHIP_ID_SD5116S,   HW_CHIP_ID_5116S_E},
		{CHIP_ID_SERIAL_VERSION_MASK, CHIP_ID_SD5116H,   HW_CHIP_ID_5116H_E},
		{CHIP_ID_SERIAL_VERSION_MASK, CHIP_ID_SD5116T,   HW_CHIP_ID_5116T_E},
		{CHIP_ID_SERIAL_VERSION_MASK, CHIP_ID_SD5116TV5, HW_CHIP_ID_5116TV5_E},
		{CHIP_ID_SERIAL_VERSION_MASK, CHIP_ID_SD5116L,   HW_CHIP_ID_5116L_E},
		{CHIP_ID_SUB_VERSION_MASK,    CHIP_ID_SD5118,    HW_CHIP_ID_5118_E},
		{CHIP_ID_SUB_VERSION_MASK,    CHIP_ID_SD5118V2,  HW_CHIP_ID_5118V2_E},
		{CHIP_ID_SERIAL_VERSION_MASK, CHIP_ID_SD5117H,   HW_CHIP_ID_5117H_E},
		{CHIP_ID_SERIAL_VERSION_MASK, CHIP_ID_SD5117P,   HW_CHIP_ID_5117P_E},
		{CHIP_ID_SERIAL_VERSION_MASK, CHIP_ID_SD5117V,   HW_CHIP_ID_5117V_E},
		{CHIP_ID_SERIAL_VERSION_MASK, CHIP_ID_SD5610H,   HW_CHIP_ID_5610H_E},
		{CHIP_ID_SERIAL_VERSION_MASK, CHIP_ID_SD5610T,   HW_CHIP_ID_5610T_E},
		{CHIP_ID_SERIAL_VERSION_MASK, CHIP_ID_SD5117L,   HW_CHIP_ID_5117L_E},
		{CHIP_ID_SERIAL_VERSION_MASK, CHIP_ID_SD5182H,   HW_CHIP_ID_5182H_E},
		{CHIP_ID_SERIAL_VERSION_MASK, CHIP_ID_SD5182S,   HW_CHIP_ID_5182S_E},
		{CHIP_ID_SUB_VERSION_MASK,    CHIP_ID_SD5117V_V6, HW_CHIP_ID_5117V_E},
		{CHIP_ID_SUB_VERSION_MASK,    CHIP_ID_SD5117P_V5, HAL_CHIP_ID_5117PV5},
		{CHIP_ID_ALL_VERSION_MASK,    CHIP_ID_SD5182H_V5, HAL_CHIP_ID_5182HV5},
		{CHIP_ID_SUB_VERSION_MASK,    CHIP_ID_SD5182T,   HAL_CHIP_ID_5182T},
		{CHIP_ID_SERIAL_VERSION_MASK, CHIP_ID_SD5182S_V5, HAL_CHIP_ID_5182SV5},
		{CHIP_ID_SUB_VERSION_MASK,    CHIP_ID_SD5182TCS, HAL_CHIP_ID_5182T},
		{CHIP_ID_SERIAL_VERSION_MASK, CHIP_ID_SD1156H,   HAL_CHIP_ID_1156H},
		{CHIP_ID_SERIAL_VERSION_MASK, CHIP_ID_SD5190,    HAL_CHIP_ID_5190},
		{CHIP_ID_SERIAL_VERSION_MASK, CHIP_ID_SD1155H,   HAL_CHIP_ID_1155H},
		{CHIP_ID_SERIAL_VERSION_MASK, CHIP_ID_SD1156E,   HAL_CHIP_ID_1156E},
		{CHIP_ID_SERIAL_VERSION_MASK, CHIP_ID_SD2952H,   HAL_CHIP_ID_2952H},
	};

	ui_st_num = sizeof(st_chipid_info)/sizeof(struct hw_ker_chipid_info_s);
	pui_reg = (unsigned int *)ioremap(0x10100800, sizeof(unsigned int));

	reg_val = readl(pui_reg);
	for (ui_loop = 0; ui_loop < ui_st_num; ui_loop++) {
		if ((reg_val & st_chipid_info[ui_loop].ui_chip_mask) ==
				st_chipid_info[ui_loop].ui_chip_value) {
			ui_ret = st_chipid_info[ui_loop].em_chip_id;
			break;
		}
	}
	iounmap(pui_reg);

	return ui_ret;
}

enum HW_CHIP_ID_E get_chip_id_ont(void)
{
	if (hw_chip_id == HW_CHIP_ID_NONE_E)
		hw_chip_id = get_chip_id();

	return hw_chip_id;
}
EXPORT_SYMBOL(get_chip_id_ont);

int ehci_and_ohci_chip_id_check(void)
{
	if (hw_chip_id != HW_CHIP_ID_NONE_E) {
		unsigned int i;
		unsigned int noehci_num;

		noehci_num = sizeof(noehci_id)/sizeof(enum HW_CHIP_ID_E);
			for (i = 0; i < noehci_num; i++)
				if (hw_chip_id == noehci_id[i])
					return -ENODEV;
	}

	return 0;
}
EXPORT_SYMBOL(ehci_and_ohci_chip_id_check);

struct usb_logic_port *hw_usb_hcd_logic_port_get(void)
{
	return &g_hal_usb_logic_port;
}
EXPORT_SYMBOL(hw_usb_hcd_logic_port_get);

int hiusb_ehci_ohci_chip_id_check(struct resource *res, struct usb_logic_port *logic_port,
	struct platform_device *pdev, int type)
{
	int retval;

	retval = ehci_and_ohci_chip_id_check();
	if (retval != 0)
		return retval;

	if ((hw_chip_id == HW_CHIP_ID_5117P_E) ||
		(hw_chip_id == HAL_CHIP_ID_5117PV5) ||
		(hw_chip_id == HW_CHIP_ID_5117L_E)) {
		res[1].start = CONFIG_HIUSB_OHCI_IRQNUM_5117P;
		res[1].end = CONFIG_HIUSB_OHCI_IRQNUM_5117P;

		if (type == USB_TYPE_ONT_EHCI) {
			/*
			 * 5117P has USB 2.0 and USB 3.0. USB 2.0
			 * controller corresponding to hisi is 0
			 */
			g_hal_usb_logic_port.usb2_logic_port = 0;
#ifdef CONFIG_ARM
			/* Disable the USB ACP function */
			set_dma_ops(&pdev->dev, &arm_coherent_dma_ops);
#endif
			dev_info(&pdev->dev, "HIUSB_EHCI ACP is disable\n");
		}
	}

	return 0;
}
EXPORT_SYMBOL(hiusb_ehci_ohci_chip_id_check);
#endif

int hiusb_start_hcd_5116H(struct hi_usb_ctrl_5116_reg_s *usb_reg_ctrl)
{
	int i;
	unsigned int reg;
	unsigned long flags;

	usb_reg_ctrl->SC_RST_CTRL1 = ioremap(0x10100154, sizeof(unsigned int));
	if (usb_reg_ctrl->SC_RST_CTRL1 == NULL) {
		pr_err("ioremap error: usb_reg_ctrl->SC_RST_CTRL1 = NULL\n");
		iounmap(usb_reg_ctrl->SC_PERCTRL38);
		iounmap(usb_reg_ctrl->SC_PEREN1);
		iounmap(usb_reg_ctrl->SC_RST_CTRL0);
		iounmap(usb_reg_ctrl->IO_SEL0);
		return -1;
	}

	usb_reg_ctrl->SC_PERCTRL36 = ioremap(0x1010012c, sizeof(unsigned int));
	if (usb_reg_ctrl->SC_PERCTRL36 == NULL) {
		pr_err("ioremap error: usb_reg_ctrl->SC_PERCTRL36 = NULL\n");
		iounmap(usb_reg_ctrl->SC_PERCTRL38);
		iounmap(usb_reg_ctrl->SC_RST_CTRL0);
		iounmap(usb_reg_ctrl->IO_SEL0);
		iounmap(usb_reg_ctrl->SC_PEREN1);
		iounmap(usb_reg_ctrl->SC_RST_CTRL1);
		return -1;
	}
	usb_reg_ctrl->SC_SYSSTAT1 = ioremap(0x1010000c, sizeof(unsigned int));
	if (usb_reg_ctrl->SC_SYSSTAT1 == NULL) {
		pr_err("ioremap error: usb_reg_ctrl->SC_SYSSTAT1 = NULL\n");
		iounmap(usb_reg_ctrl->SC_PERCTRL38);
		iounmap(usb_reg_ctrl->SC_RST_CTRL0);
		iounmap(usb_reg_ctrl->SC_RST_CTRL1);
		iounmap(usb_reg_ctrl->IO_SEL0);
		iounmap(usb_reg_ctrl->SC_PEREN1);
		iounmap(usb_reg_ctrl->SC_PERCTRL36);
		return -1;
	}

	local_irq_save(flags);

	// enable USB ACP
	writel(0, usb_reg_ctrl->SC_PERCTRL36);

	mdelay(1);

	// change usb disconnect threshold
	writel(0xa011, usb_reg_ctrl->SC_RST_CTRL1);
	writel(0xa010, usb_reg_ctrl->SC_RST_CTRL1);
	writel(0xa011, usb_reg_ctrl->SC_RST_CTRL1);
	writel(0xa010, usb_reg_ctrl->SC_RST_CTRL1);

	reg = readl(usb_reg_ctrl->SC_SYSSTAT1);
	reg &= (~0x1E00);
	reg &= 0xff00;
	reg = reg >> 8;
	writel(reg << 16, usb_reg_ctrl->SC_RST_CTRL1);

	writel((reg | 0xa111), usb_reg_ctrl->SC_RST_CTRL1);
	writel((reg | 0xa110), usb_reg_ctrl->SC_RST_CTRL1);
	writel((reg | 0xa111), usb_reg_ctrl->SC_RST_CTRL1);
	writel((reg | 0xa110), usb_reg_ctrl->SC_RST_CTRL1);

	mdelay(1);

	reg = readl(usb_reg_ctrl->SC_PERCTRL38);  // usb phy insulate
	reg &= ~0x1;
	writel(reg, usb_reg_ctrl->SC_PERCTRL38);

	reg = readl(usb_reg_ctrl->SC_PEREN1);  // usb clock enable
	reg |= 0x1000;
	writel(reg, usb_reg_ctrl->SC_PEREN1);

	udelay(100);

	// usb phy / usb phy port/ usb host reset
	reg = readl(usb_reg_ctrl->SC_RST_CTRL0);
	reg &= ~0x1C000;
	writel(reg, usb_reg_ctrl->SC_RST_CTRL0);

	mdelay(1);

	// bit 15 usb phy release
	reg = readl(usb_reg_ctrl->SC_RST_CTRL0);
	reg |= 0x8000;
	writel(reg, usb_reg_ctrl->SC_RST_CTRL0);

	mdelay(2);

	reg = readl(usb_reg_ctrl->SC_RST_CTRL0);  // usb phy port reset release
	reg |= 0x10000;
	writel(reg, usb_reg_ctrl->SC_RST_CTRL0);

	udelay(1);

	reg = readl(usb_reg_ctrl->SC_RST_CTRL0);  // usb host reset release
	reg |= 0x4000;
	writel(reg, usb_reg_ctrl->SC_RST_CTRL0);

	for (i = 0; i < 4; i++) {
		writel(0xc6111, usb_reg_ctrl->SC_RST_CTRL1);
		writel(0xc6110, usb_reg_ctrl->SC_RST_CTRL1);
	}

	local_irq_restore(flags);
	return 0;
}
EXPORT_SYMBOL(hiusb_start_hcd_5116H);

int hiusb_start_hcd_5116T(struct hi_usb_ctrl_5116_reg_s *usb_reg_ctrl)
{
	int i;
	unsigned int reg;
	unsigned int *reg_addr;
	unsigned long flags;

	usb_reg_ctrl->SC_RST_CTRL1 = ioremap(0x10168030, sizeof(unsigned int));
	if (usb_reg_ctrl->SC_RST_CTRL1 == NULL) {
		pr_err("ioremap error: usb_reg_ctrl->SC_RST_CTRL1 = NULL\n");
		iounmap(usb_reg_ctrl->SC_PERCTRL38);
		iounmap(usb_reg_ctrl->SC_PEREN1);
		iounmap(usb_reg_ctrl->SC_RST_CTRL0);
		iounmap(usb_reg_ctrl->IO_SEL0);
		return -1;
	}

	usb_reg_ctrl->FUNCTION_PIN_SEL = ioremap(0x14900208, sizeof(unsigned int));
	if (usb_reg_ctrl->FUNCTION_PIN_SEL == NULL) {
		pr_err("ioremap error: usb_reg_ctrl->IO_SEL0 = NULL\n");
		iounmap(usb_reg_ctrl->SC_PERCTRL38);
		iounmap(usb_reg_ctrl->SC_PEREN1);
		iounmap(usb_reg_ctrl->SC_RST_CTRL0);
		iounmap(usb_reg_ctrl->IO_SEL0);
		iounmap(usb_reg_ctrl->SC_RST_CTRL1);
		return -1;
	}

	// repair Teclast and Toshiba USB flash disk read reset error
	reg_addr = (unsigned int *)ioremap(0x10a5c10c, sizeof(unsigned int));
	if (reg_addr == NULL) {
		pr_err("ioremap error: 0x10a5c10c remap NULL!");
		iounmap(usb_reg_ctrl->SC_PERCTRL38);
		iounmap(usb_reg_ctrl->SC_PEREN1);
		iounmap(usb_reg_ctrl->SC_RST_CTRL0);
		iounmap(usb_reg_ctrl->IO_SEL0);
		iounmap(usb_reg_ctrl->SC_RST_CTRL1);
		iounmap(usb_reg_ctrl->FUNCTION_PIN_SEL);
		return -1;
	}

	/* gpio17 - gpio18, which are used as PCIe reset signals
		* on the product board and do not need to be processed here
		*/

	local_irq_save(flags);

	// Whether to enable the USB0 interface signal and whether to affect USB_EN
	// select usb3 function;IOMUX_DIO.FUNCTION_PIN_SEL[17] =1'b1
	reg = readl(usb_reg_ctrl->FUNCTION_PIN_SEL);
	reg |= 0x20000;
	writel(reg, usb_reg_ctrl->FUNCTION_PIN_SEL);

	// usb port 0 & 1 & ctrl reset :0x1488002c
	// [29:28] = 2'b00 [13:12] = 2'b00 [14] = 1'b0
	reg = readl(usb_reg_ctrl->SC_RST_CTRL0);
	reg &= ~0x60007000;
	writel(reg, usb_reg_ctrl->SC_RST_CTRL0);

	// Whether to enable the USB1 interface signal and whether to affect USB_EN
	// select usb2 function;IOMUX_DIO.FUNCTION_PIN_SEL[20] =1'b1
	reg = readl(usb_reg_ctrl->FUNCTION_PIN_SEL);
	reg |= 0x100000;
	writel(reg, usb_reg_ctrl->FUNCTION_PIN_SEL);

	// open usb controller/usb2 phy/usb3 phy clock:0x14880020 bit[12 17 18 ]set 1
	reg = readl(usb_reg_ctrl->SC_PEREN1);
	reg |= 0x61000;
	writel(reg, usb_reg_ctrl->SC_PEREN1);
	udelay(20);

	// usb3 phy port unreset
	reg = readl(usb_reg_ctrl->SC_RST_CTRL0);
	reg |= 0x10000000;
	writel(reg, usb_reg_ctrl->SC_RST_CTRL0);
	udelay(50);

	// usb3 phy pipe unreset
	reg = readl(usb_reg_ctrl->SC_RST_CTRL0);
	reg |= 0x20000000;
	writel(reg, usb_reg_ctrl->SC_RST_CTRL0);

	// usb2 phy port unreset
	reg = readl(usb_reg_ctrl->SC_RST_CTRL0);
	reg |= 0x1000;
	writel(reg, usb_reg_ctrl->SC_RST_CTRL0);
	udelay(50);

	// usb2 phy utmi unreset
	reg = readl(usb_reg_ctrl->SC_RST_CTRL0);
	reg |= 0x2000;
	writel(reg, usb_reg_ctrl->SC_RST_CTRL0);
	udelay(10);

	// usb controller unreset
	reg = readl(usb_reg_ctrl->SC_RST_CTRL0);
	reg |= 0x4000;
	writel(reg, usb_reg_ctrl->SC_RST_CTRL0);
	udelay(10);

	for (i = 0; i < 4; i++) {  // disconnect threshold config
		writel(0xc6111, usb_reg_ctrl->SC_RST_CTRL1);
		writel(0xc6110, usb_reg_ctrl->SC_RST_CTRL1);
	}

	reg = readl(reg_addr);
	reg &= (~0x2ff80000);
	reg |= 0x21080000;
	writel(reg, reg_addr);

	local_irq_restore(flags);
	iounmap(reg_addr);
	return 0;
}
EXPORT_SYMBOL(hiusb_start_hcd_5116T);

static int __init hiusb_ont_init(void)
{
	/* init soc chip type */
	(void)get_chip_id_ont();

	/* set usb3 logic port */
	memset(&g_hal_usb_logic_port, 0, sizeof(g_hal_usb_logic_port));
	if ((hw_chip_id == HW_CHIP_ID_5117P_E) || (hw_chip_id == HAL_CHIP_ID_5117PV5) ||
	(hw_chip_id == HAL_CHIP_ID_1156H) || (hw_chip_id == HAL_CHIP_ID_1156E) ||
	(hw_chip_id == HAL_CHIP_ID_1155H) || (hw_chip_id == HAL_CHIP_ID_2952H))
		g_hal_usb_logic_port.usb3_logic_port = 1;

	pr_info("hiusb_ont init\n");
	return 0;
}
module_init(hiusb_ont_init);

static void __exit hiusb_ont_exit(void)
{
	pr_info("hiusb_ont exit\n");
}
module_exit(hiusb_ont_exit);

MODULE_LICENSE("GPL");
